use anyhow::Result;
use log::{debug, info, warn};
use spider_rs::website::Website;
use spider_rs::page::Page;
use std::time::Duration;
use std::path::Path;
use std::fs;
use std::io::Write;
use url::Url;

/// 爬虫结果结构体，包含爬取到的内容和对应的URL
#[derive(Debug, Clone)]
pub struct CrawlResult {
    /// 页面内容（HTML）
    pub content: String,
    /// 页面URL
    pub url: String,
}

/// 爬虫配置结构体，用于配置爬虫行为
#[derive(Debug, Clone)]
pub struct CrawlConfiguration {
    /// 是否遵循 robots.txt 规则
    pub respect_robots_txt: bool,
    /// 是否爬取子域名
    pub subdomains: bool,
    /// 是否爬取所有顶级域名
    pub tld: bool,
    /// 请求之间的延迟（毫秒）
    pub delay: u64,
    /// 爬取深度限制
    pub depth: usize,
    /// 爬取页面数量限制
    pub page_limit: u32,
    /// 请求超时时间（秒）
    pub timeout: u64,
    /// 用户代理字符串
    pub user_agent: Option<String>,
    /// 是否显示详细日志
    pub verbose: bool,
    /// 白名单URL列表，只爬取匹配这些模式的URL
    pub whitelist_url: Option<Vec<String>>,
    /// 黑名单URL列表，不爬取匹配这些模式的URL
    pub blacklist_url: Option<Vec<String>>,
}

impl Default for CrawlConfiguration {
    fn default() -> Self {
        Self {
            respect_robots_txt: true,
            subdomains: false,
            tld: false,
            delay: 100,
            depth: 2,
            page_limit: 100,
            timeout: 30,
            user_agent: None,
            verbose: false,
            whitelist_url: None,
            blacklist_url: None,
        }
    }
}

/// 爬虫客户端，封装爬虫功能
pub struct SpiderClient {
    /// 爬虫配置
    config: CrawlConfiguration,
    /// 基础URL
    base_url: String,
    /// 网站实例
    website: Website,
}

impl SpiderClient {
    /// 创建一个新的爬虫客户端
    ///
    /// # 参数
    ///
    /// * `base_url` - 基础URL，爬虫将从这个URL开始爬取
    /// * `config` - 可选的爬虫配置，如果不提供则使用默认配置
    ///
    /// # 返回值
    ///
    /// 返回一个新的爬虫客户端实例
    pub fn new(base_url: &str, config: Option<CrawlConfiguration>) -> Self {
        let config = config.unwrap_or_else(|| {
            let mut default_config = CrawlConfiguration::default();
            default_config.respect_robots_txt = false;
            default_config.subdomains = true;
            default_config.tld = false;
            default_config
        });

        let mut website = Website::new(base_url);
        Self::apply_config(&mut website, &config);

        Self {
            config,
            base_url: base_url.to_string(),
            website,
        }
    }

    /// 应用配置到网站实例
    ///
    /// # 参数
    ///
    /// * `website` - 网站实例
    /// * `config` - 爬虫配置
    fn apply_config(website: &mut Website, config: &CrawlConfiguration) {
        // 应用基本配置
        website.configuration.respect_robots_txt = config.respect_robots_txt;
        website.configuration.subdomains = config.subdomains;
        website.configuration.tld = config.tld;
        website.configuration.delay = config.delay;

        // 设置深度和页面限制
        website.with_depth(config.depth);
        website.with_limit(config.page_limit);

        // 设置请求超时
        website.with_request_timeout(Some(Duration::from_secs(config.timeout)));

        // 设置用户代理
        if let Some(user_agent) = &config.user_agent {
            website.with_user_agent(Some(user_agent));
        }

        // 暂时不设置白名单和黑名单URL
        // Spider库的with_whitelist_url和with_blacklist_url方法需要特定类型
        // 如果需要使用，需要引入相应的类型并进行转换
    }

    /// 爬取网站
    ///
    /// # 返回值
    ///
    /// 返回一个包含爬取结果的向量，每个结果包含页面内容和URL
    pub async fn crawl(&mut self) -> Result<Vec<CrawlResult>> {
        if self.config.verbose {
            info!("正在爬取网站: {}", self.base_url);
        } else {
            debug!("爬取网站: {}", self.base_url);
        }

        // 开始爬取
        self.website.scrape().await;

        // 获取爬取结果
        let empty_vec = vec![];
        let pages = self.website.get_pages().unwrap_or(&empty_vec);

        if !pages.is_empty() {
            if self.config.verbose {
                info!("共爬取到 {} 个页面", pages.len());
            } else {
                debug!("爬取到 {} 个页面", pages.len());
            }
        } else {
            warn!("未爬取到任何页面");
        }

        // 转换为CrawlResult向量
        let results = pages
            .iter()
            .map(|page| CrawlResult {
                content: page.get_html().to_string(),
                url: page.get_url().to_string(),
            })
            .collect();

        Ok(results)
    }

    /// 爬取网站并保存所有页面到指定目录
    ///
    /// # 参数
    ///
    /// * `output_dir` - 输出目录路径，爬取的页面将保存到这个目录
    ///
    /// # 返回值
    ///
    /// 返回保存的页面数量和总爬取的页面数量
    pub async fn crawl_and_save(&mut self, output_dir: &Path) -> Result<(usize, usize)> {
        // 确保输出目录存在
        if !output_dir.exists() {
            fs::create_dir_all(output_dir)?;
            if self.config.verbose {
                info!("创建目录: {}", output_dir.display());
            } else {
                debug!("创建目录: {}", output_dir.display());
            }
        }

        // 爬取网站
        let results = self.crawl().await?;

        if results.is_empty() {
            warn!("未找到任何页面内容");
            return Ok((0, 0));
        }

        let total_pages = results.len();

        // 保存所有页面
        let mut saved_count = 0;

        for result in &results {
            if let Some(file_name) = save_result_to_file(result, &self.base_url, output_dir)? {
                saved_count += 1;
                if self.config.verbose {
                    info!("保存页面 ({}/{}): {}", saved_count, total_pages, file_name);
                } else {
                    debug!("保存页面 ({}/{}): {}", saved_count, total_pages, file_name);
                }
            }
        }

        if self.config.verbose {
            info!("爬取完成! 共保存 {} 个页面到 {}", saved_count, output_dir.display());
        } else {
            debug!("爬取完成! 共保存 {} 个页面到 {}", saved_count, output_dir.display());
        }

        Ok((saved_count, total_pages))
    }

    /// 获取爬虫配置
    pub fn get_config(&self) -> &CrawlConfiguration {
        &self.config
    }

    /// 设置爬虫配置
    pub fn set_config(&mut self, config: CrawlConfiguration) {
        Self::apply_config(&mut self.website, &config);
        self.config = config;
    }

    /// 更新爬虫配置
    pub fn update_config<F>(&mut self, updater: F)
    where
        F: FnOnce(&mut CrawlConfiguration),
    {
        let mut config = self.config.clone();
        updater(&mut config);
        self.set_config(config);
    }
}

/// 通用爬虫方法，根据给定的配置和基础URL爬取网页
///
/// # 参数
///
/// * `configuration` - 爬虫配置
/// * `base_url` - 基础URL，爬虫将从这个URL开始爬取
///
/// # 返回值
///
/// 返回一个包含爬取结果的向量，每个结果包含页面内容和URL
///
/// # 示例
///
/// ```rust
/// use crate::spider_utils::{crawl_website, CrawlConfiguration};
///
/// #[tokio::main]
/// async fn main() -> anyhow::Result<()> {
///     let config = CrawlConfiguration::default();
///     let results = crawl_website(config, "https://example.com").await?;
///
///     for result in results {
///         println!("URL: {}", result.url);
///         println!("Content length: {}", result.content.len());
///     }
///
///     Ok(())
/// }
/// ```
pub async fn crawl_website(configuration: CrawlConfiguration, base_url: &str) -> Result<Vec<CrawlResult>> {
    let mut client = SpiderClient::new(base_url, Some(configuration));
    client.crawl().await
}

/// 爬取单个页面，返回页面内容和URL
///
/// # 参数
///
/// * `url` - 要爬取的页面URL
/// * `timeout` - 请求超时时间（秒）
///
/// # 返回值
///
/// 返回爬取结果，包含页面内容和URL
pub async fn crawl_single_page(url: &str, timeout: u64) -> Result<CrawlResult> {
    let mut config = CrawlConfiguration::default();
    config.page_limit = 1;
    config.depth = 0;
    config.timeout = timeout;

    let results = crawl_website(config, url).await?;

    match results.into_iter().next() {
        Some(result) => Ok(result),
        None => Err(anyhow::anyhow!("Failed to crawl page: {}", url)),
    }
}

/// 爬取网站并保存所有页面到指定目录
///
/// # 参数
///
/// * `base_url` - 基础URL，爬虫将从这个URL开始爬取
/// * `output_dir` - 输出目录路径，爬取的页面将保存到这个目录
/// * `config` - 可选的爬虫配置，如果不提供则使用默认配置
///
/// # 返回值
///
/// 返回保存的页面数量和总爬取的页面数量
///
/// # 示例
///
/// ```rust
/// use crate::spider_utils::crawl_and_save_website;
/// use std::path::Path;
///
/// #[tokio::main]
/// async fn main() -> anyhow::Result<()> {
///     let base_url = "https://example.com";
///     let output_dir = Path::new("output");
///
///     let (saved_count, total_count) = crawl_and_save_website(base_url, output_dir, None).await?;
///     println!("保存了 {}/{} 个页面", saved_count, total_count);
///
///     Ok(())
/// }
/// ```
pub async fn crawl_and_save_website(
    base_url: &str,
    output_dir: &Path,
    config: Option<CrawlConfiguration>,
) -> Result<(usize, usize)> {
    let mut client = SpiderClient::new(base_url, config);

    // 启用详细日志
    client.update_config(|config| {
        config.verbose = true;
    });

    client.crawl_and_save(output_dir).await
}

/// 保存爬取结果到文件
///
/// # 参数
///
/// * `result` - 爬取结果
/// * `base_url` - 基础URL
/// * `output_dir` - 输出目录路径
///
/// # 返回值
///
/// 如果成功保存，返回文件名；否则返回None
fn save_result_to_file(result: &CrawlResult, base_url: &str, output_dir: &Path) -> Result<Option<String>> {
    // 只处理来自基础URL的页面
    if !result.url.starts_with(base_url) {
        return Ok(None);
    }

    // 获取相对路径作为文件名
    let parsed_url = Url::parse(&result.url)?;
    let path = parsed_url.path();

    // 提取文件名
    let file_name = if path.ends_with('/') || path == "/" {
        "index.html".to_string()
    } else {
        Path::new(path)
            .file_name()
            .map(|f| f.to_string_lossy().to_string())
            .unwrap_or_else(|| "index.html".to_string())
    };

    // 创建文件
    let file_path = output_dir.join(&file_name);
    let mut file = fs::File::create(&file_path)?;

    // 写入HTML内容
    file.write_all(result.content.as_bytes())?;

    Ok(Some(file_name))
}

/// 保存单个页面到文件
///
/// # 参数
///
/// * `page` - 页面对象
/// * `base_url` - 基础URL
/// * `output_dir` - 输出目录路径
///
/// # 返回值
///
/// 如果成功保存，返回文件名；否则返回None
#[allow(dead_code)]
fn save_page_to_file(page: &Page, base_url: &str, output_dir: &Path) -> Result<Option<String>> {
    let result = CrawlResult {
        content: page.get_html().to_string(),
        url: page.get_url().to_string(),
    };

    save_result_to_file(&result, base_url, output_dir)
}
